﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Drawing;

namespace PolyToPoly_Subdivision.Geometry
{
    public class Polygon
    {
        //private var _vertices : Vector.<Vertex> = new Vector.<Vertex>();
        private List<Vertex> verticles = new List<Vertex>();
        /// <summary>
        /// 
        /// </summary>
        public Polygon()
        { }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="Verticles"></param>
        public Polygon(double[] Verticles)
        {
            if (((double)Verticles.Length / 2) % 1 == 0)
            {
                for (int i = 0; i < Verticles.Length; i += 2)
                {
                    AddVertex(Verticles[i],Verticles[i+1]);
                }
            }
            else
            {
                throw new Exception("Verticles.Length not power of two");

            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="pointX"></param>
        /// <param name="pointY"></param>
        public void AddVertex(double pointX, double pointY)
        {
            verticles.Add(new Vertex(pointX, pointY));
        }
        //        
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public double averageSideLength()
        {
            double l = 0.0f, dx,dy;
            int n = verticles.Count;
            Vertex v1, v2;
            //
            for (int i = 0; i < n; ++i)
            {
                v1 = verticles[i];
                v2 = verticles[i + 1];

                dx = v2.x - v1.x;
                dy = v2.y - v1.y;

                l += dx * dx + dy * dy;
            }
            //
            return Math.Sqrt(l) / Convert.ToDouble(n);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="useLongestSidesProb"></param>
        /// <param name="useRandomPointsProb"></param>
        /// <returns></returns>
        public List<Polygon> subdivide(float useLongestSidesProb, float useRandomPointsProb)
        {

            Vertex v;
            double lp;
            int li1, li2;
            Vertex v1, v2;
            //

            //num of sides
            int ns = verticles.Count;
            int mx = ns - 1;

            Random rand = new Random();
            bool useLongestSides = rand.NextDouble() < useLongestSidesProb;
            bool useRandomPoints = rand.NextDouble() < useRandomPointsProb;

            if (useLongestSides)
            {
                //Dictionary<int, double> sort = new Dictionary<int, double>();
                double dx, dy;
                //
                int max1Counter = 0;
                int max2Counter = 0;
                double max1 = 0;
                double max2 = 0;
                double dXY = 0;
                //
                for (int i = 0; i < ns; i++)
                {
                    v1 = verticles[i];
                    v2 = verticles[(i + 1) % ns];
                    //
                    dx = v2.x - v1.x;
                    dy = v2.y - v1.y;
                    //
                    //sort.Add(i, (dx * dx + dy * dy));
                    dXY = (dx * dx + dy * dy);
                    if (dXY > max1) { max1 = dXY; max1Counter = i; }
                    else if (dXY < max1 && dXY > max2) { max2 = dXY; max2Counter = i; }
                }

                //var _sort = sort.OrderByDescending(x => x.Value);
                li1 = max1Counter;
                li2 = max2Counter;
                //li1 = _sort.ElementAt(0).Key;
                //li2 = _sort.ElementAt(1).Key;
            }
            else
            {
                li1 = rand.Next(mx);
                li2 = rand.Next(mx);
                while (li2 == li1) { li2 = rand.Next(mx); }
            }
            //Cross start
            lp = useRandomPoints ? rand.NextDouble() : 0.5;
            v1 = verticles[li1];
            v2 = verticles[(li1 + 1) % ns];

            double cx1 = v1.x + (v2.x - v1.x) * lp;
            double cy1 = v1.y + (v2.y - v1.y) * lp;
            
            //Cross end
            lp = useRandomPoints ? rand.NextDouble() : 0.5;
            v1 = verticles[li2];
            v2 = verticles[(li2 + 1) % ns];

            double cx2 = v1.x + (v2.x - v1.x) * lp;
            double cy2 = v1.y + (v2.y - v1.y) * lp;

            /*
             * Create the first subdivision
             * cross start around to cross end
             */

            Polygon p1 = new Polygon();
            p1.AddVertex(cx1, cy1);

            int n1 = (li1 + 1) % ns;
            int n2 = (li2 + 1) % ns;

            int j = n1;
            while (j != n2)
            {
                v = verticles[j];
                p1.AddVertex(v.x, v.y);
                j = (j + 1) % ns;
            }

            p1.AddVertex(cx2, cy2);

            /*
             * Create the second subdivision
             * cross end around to cross start
             */

            Polygon p2 = new Polygon();
            p2.AddVertex(cx2, cy2);

            j = n2;

            while(j!=n1)
            {
                v = verticles[j];
                p2.AddVertex(v.x, v.y);
                j = (j + 1) % ns;
            }

            p2.AddVertex(cx1, cy1);

            //return new polys
            List<Polygon> result = new List<Polygon>(2);
            
            result.Add(p1);
            result.Add(p2);
            //
            return result;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public List<Vertex> GetVerticles()
        {
            return verticles;
        }
        //
        public PointF[] GetVerticlesAsPoints()
        {
            //
            PointF[] result = new PointF[verticles.Count];
            //
            int i = 0;
            foreach (Vertex vertex in verticles)
            {
                result[i] = new PointF((float)vertex.x, (float)vertex.y);
                i++;
            }
            //
            return result;
        }
    }
}
